package com.content.scaffold.common.utils;

import com.content.scaffold.common.exception.BaseException;
import org.apache.commons.lang3.StringUtils;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.StringReader;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

/**
 * @author wuxin
 */
public class RSAUtils {


    public static String decrypt(String text, String privateKey) {

        if (StringUtils.isBlank(text)) {
            return null;
        }
        PrivateKey key = loadPrivateKey(privateKey);
        String desStr;
        try {
            byte[] plainTextBuffer = RSAUtils.decrypt(text, key);
            desStr = new String(plainTextBuffer, StandardCharsets.UTF_8);
        } catch (Exception e) {
            throw new BaseException("解密失败(非对称密码解密失败)", e);
        }
        return desStr;
    }

    public static byte[] encrypt(String text, Key key) {
        try {
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE, key);
            byte[] textBuffer = text.getBytes(StandardCharsets.UTF_8);
            return doCipherFinal(cipher, textBuffer, 117);
        } catch (Exception e) {
            throw new BaseException("decrypt failed", e);
        }
    }

    public static byte[] decrypt(String base64Text, Key key) {
        try {
            byte[] buffer = Base64.getDecoder().decode(base64Text);
//            byte[] buffer = Base64.getMimeDecoder().decode(base64Text);

            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE, key);
            return doCipherFinal(cipher, buffer, 128);
        } catch (Exception e) {
            throw new BaseException("decrypt failed", e);
        }
    }

    public static PrivateKey loadPrivateKey(String base64PrivateKey) {
        try {
            byte[] keyBuf = readKey(base64PrivateKey);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            KeySpec keySpec = new PKCS8EncodedKeySpec(keyBuf);
            return keyFactory.generatePrivate(keySpec);
        } catch (IOException | NoSuchAlgorithmException | InvalidKeySpecException e) {
            throw new BaseException("load key failed", e);
        }
    }

    public static PublicKey loadPublicKey(String base64PublicKey) {
        try {
            byte[] keyBuf = readKey(base64PublicKey);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            KeySpec keySpec = new X509EncodedKeySpec(keyBuf);
            return keyFactory.generatePublic(keySpec);
        } catch (IOException | NoSuchAlgorithmException | InvalidKeySpecException e) {
            throw new BaseException("load key failed", e);
        }
    }

    private static byte[] readKey(String keyText) throws IOException {
        BufferedReader reader = new BufferedReader(new StringReader(keyText));
        StringBuilder buf = new StringBuilder();
        String line;
        while ((line = reader.readLine()) != null) {
            if (line.startsWith("-----")) {
                continue;
            }
            buf.append(line);
        }
        return Base64.getDecoder().decode(buf.toString());
    }

    private static byte[] doCipherFinal(Cipher cipher, byte[] buf, int bufSize) throws BadPaddingException, IllegalBlockSizeException, IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int index = 0;
        int length = buf.length;
        while (index < length) {
            int size = Math.min(bufSize, length - index);
            baos.write(cipher.doFinal(buf, index, size));
            index = index + size;
        }
        return baos.toByteArray();
    }

}
